/*
 * File:   PhysicsLogic.h
 * Author: RedEyedKiller
 *
 * Created on 22 Σεπτέμβριος 2010, 7:09 μμ
 */

#ifndef PHYSICSLOGIC_H
#define	PHYSICSLOGIC_H


#include "AbstractLogic.h"

#include "physics/PhysicalObject.h"
#include "physics/PhysicsMaterial.h"
#include "Rect.h"
#include "RenderingManager.h"
#include <list>

//forward declaration
namespace physicsSystem
{
class PhysicsManager;

namespace PhysicsInternals
{
class EntityContactInfo;
struct TileContactInfo;
}
}

class ScriptRegister;

namespace EntitySystem
{

/**
 * This class handles all physics events as collision detection and collision
 * (physics) resolution.
 */
class PhysicsLogic : public AbstractLogic, public physicsSystem::PhysicalObject
{
public:
    PhysicsLogic();
    PhysicsLogic(const PhysicsLogic& org);
    virtual PhysicsLogic* Clone();
    virtual ~PhysicsLogic();

    /**
     * Updates physical object's state using the equations of kinetics.
     * @param seconds the time passed since the last Update call.
     */
    virtual void Update(float seconds);

    Math::IntersectionType CollisionCheck(PhysicsLogic* pl, Math::Vector2F& axisInfo);

    //
    //Methods called when this collided with something else
    //

    /**
     * This is called when this has collided with one entity. Note that this method will not change
     * object's state. It will only call the needed scripts.
     * For solid collision call OnSolidEntityCollision.
     * @param collided - The entity which collided with this.
     * @param contactNormal - A normal vector which shows the direction of the collision.
     */
    void OnEntityCollision(Entity* collided, const Math::Vector2F& contactNormal);

    /**
     * This is called when this collided with a tile. 
     * @param tile - The tile property of this tile.
     * @param contactNormal - The direction of the collision.
     */
    void OnTileCollision(std::string& tile, Math::Vector2F& contactNormal);

    /**
     * Used by contact resolve algorithm in CollisionHandler.
     * Moves this by unitsPerImass * this->invertedMass.
     * @param unitsPerImass
     */
    void AdjustPositionPerImass(const Math::Vector2F& unitsPerImass);

    /**
     * Used by contact resolve algorithm in CollisionHandler.
     * Chenges this' velocity by unitsPerImass * this->invertedMass.
     * @param unitsPerImass
     */
    void AdjustVelocityPerImass(const Math::Vector2F& unitsPerImass);

    bool HasSolidContactOn(const Math::Vector2F& direction);

    /**
     * Returns true if this has a collision on the given direction.
     * Direction should be a normal vector.
     * @param direction
     * @return 
     */
    bool HasEntityContactOn(const Math::Vector2F& direction);

    /**
     * Returns true if this has a collision on the given direction with a tile 
     * of the specified flag.
     * Direction should be a normal vector.
     * @param direction
     * @param flag
     * @return 
     */
    bool HasTileContactOn(const Math::Vector2F& direction, int flag);
    bool HasTileContactOn(const Math::Vector2F& direction,const std::string& flag);

    virtual Math::Vector2F GetPosition() const;
    //get and set
    Math::Rect* GetRect();

    void SetRect(const Math::Rect& rect);
    void AdjustPosition(float x, float y);

    const Math::Vector2F GetOldPosition() const;
    const Math::Vector2F GetOldVelocity() const;

    void SetGroupIndex(int groupIndex);
    int GetGroupIndex() const;

    void SetMaterial(const physicsSystem::PhysicsMaterial& material);
    physicsSystem::PhysicsMaterial GetMaterial() const;

    void SetIgnoreGeneralForces(bool value);
    bool IsIgnoreGeneralForces() const;

    void ApplyDampingOnX(bool dampingOnX);
    bool IsDampingOnX() const;

    void ApplyDampingOnY(bool dampingOnY);
    bool IsDampingOnY() const;

    bool IsAwake() const;

    void SetCanSleep(bool canSleep);

    float GetMotion() const;

    /**
     * Awakes or set the 
     */
    void Awake();

    /**
     * Clears contact list.
     */
    void ResetContactList();

    /**
     * Add a physics logic object int the contect list
     */
    void AddEntityContact(physicsSystem::PhysicsInternals::EntityContactInfo* toAdd);

    /**
     * Add a physics logic object int the contect list
     */
    void AddTileContact(physicsSystem::PhysicsInternals::TileContactInfo* toAdd);

    /**
     * 
     * @return the next contact struc or null if none else exists.
     */
    physicsSystem::PhysicsInternals::EntityContactInfo* GetNextContact();

    /**
     * Removes the contact with the given entity (if any)
     */
    void DeleteContact(EntitySystem::Entity* toKill);


    /**
     * Sets this logic's physics manager.
     *
     * @param manager
     */

    void SetPhysicsManager(physicsSystem::PhysicsManager *manager);


protected:
    /**
     * this rect encloses the position and the width/height of the entity.
     * Acts like a bounding shape
     */
    Math::Rect rect;

    /**
     * The old velocity of object. OBSOLETE
     */
    Math::Vector2F oldVelocity;

    /**
     * The old position of object. OBSOLETE
     */
    Math::Vector2F oldPosition;

    /**
     * An object that encapsulates some material based constants.
     */
    physicsSystem::PhysicsMaterial material;

    /**
     * Total motion this body has.
     * Used to determine if this should sleep or not.
     */
    float motion;

    /**
     * A boolean indicating if this body can sleep.
     * Bodies like player controlled objects should stay awake.
     */
    bool canSleep;

    /**
     * A boolean indicating if this body sleeping. A sleeping body is not updated
     * for performance reasons.
     */
    bool awake;

    /**
     * If true general accelaration will be ignored.
     */
    bool ignoreGeneralForces;

    /**
     * If true damping will not be aplied on velocity.
     */
    bool dampingOnX, dampingOnY;

    /**
     * Shows the collision group this object belongs into.
     */
    int groupIndex;

private:

    typedef std::list<physicsSystem::PhysicsInternals::EntityContactInfo*> EntityInfoList;
    typedef std::list<physicsSystem::PhysicsInternals::TileContactInfo*> TileInfoList;
    /**
     * Adds the result of a force into ΣF (forces) of this object.
     * This method is created to be used only by scrips.
     * @forceToAdd
     */
    void scAddForce(const Math::Vector2F &forceToAdd);

    /**
     * Returns the velocity of this physical object.
     * This method is created to be used only by scrips.
     * @return 
     */
    Math::Vector2F scGetVelocity();

    /**
     * Sets the velocity of this.
     * @param velocity
     * This method is created to be used only by scripts.
     */
    void scSetVelocity(const Math::Vector2F &velocity);

    /**
     * A list containing all physics logic objects this is in touch with.
     */
    EntityInfoList entContactList;

    /**
     * A list containing all tiles this is in contact with.
     */
    TileInfoList tileContactList;

    /**
     * Used in conjuction with GetNextContact.
     */
    EntityInfoList::iterator entEontactIterator;

    physicsSystem::PhysicsManager *registeredTo;
};

};

#endif	/* PHYSICSLOGIC_H */

